
/*
 * Copyright 2002-2023 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*
 *版权所有2002-2023原作者。
 *
 *根据Apache许可证2.0版（“许可证”）获得许可；
 *除非符合许可证，否则不得使用此文件。
 *您可以在以下地址获取许可证副本：
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 *除非适用法律要求或书面同意，软件
 *根据许可证分发的内容是按“原样”分发的，
 *无任何明示或暗示的保证或条件。
 *有关管理权限和
 *许可证下的限制。
 */

package org.springframework.beans.factory.config;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeansException;
import org.springframework.beans.PropertyAccessorFactory;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

/**
 * {@link FactoryBean} that evaluates a property path on a given target object.
 *
 * <p>The target object can be specified directly or via a bean name.
 *
 * <p>Usage examples:
 *
 * <pre class="code">&lt;!-- target bean to be referenced by name --&gt;
 * &lt;bean id="tb" class="org.springframework.beans.TestBean" singleton="false"&gt;
 *   &lt;property name="age" value="10"/&gt;
 *   &lt;property name="spouse"&gt;
 *     &lt;bean class="org.springframework.beans.TestBean"&gt;
 *       &lt;property name="age" value="11"/&gt;
 *     &lt;/bean&gt;
 *   &lt;/property&gt;
 * &lt;/bean&gt;
 *
 * &lt;!-- will result in 12, which is the value of property 'age' of the inner bean --&gt;
 * &lt;bean id="propertyPath1" class="org.springframework.beans.factory.config.PropertyPathFactoryBean"&gt;
 *   &lt;property name="targetObject"&gt;
 *     &lt;bean class="org.springframework.beans.TestBean"&gt;
 *       &lt;property name="age" value="12"/&gt;
 *     &lt;/bean&gt;
 *   &lt;/property&gt;
 *   &lt;property name="propertyPath" value="age"/&gt;
 * &lt;/bean&gt;
 *
 * &lt;!-- will result in 11, which is the value of property 'spouse.age' of bean 'tb' --&gt;
 * &lt;bean id="propertyPath2" class="org.springframework.beans.factory.config.PropertyPathFactoryBean"&gt;
 *   &lt;property name="targetBeanName" value="tb"/&gt;
 *   &lt;property name="propertyPath" value="spouse.age"/&gt;
 * &lt;/bean&gt;
 *
 * &lt;!-- will result in 10, which is the value of property 'age' of bean 'tb' --&gt;
 * &lt;bean id="tb.age" class="org.springframework.beans.factory.config.PropertyPathFactoryBean"/&gt;</pre>
 *
 * <p>If you are using Spring 2.0 and XML Schema support in your configuration file(s),
 * you can also use the following style of configuration for property path access.
 * (See also the appendix entitled 'XML Schema-based configuration' in the Spring
 * reference manual for more examples.)
 *
 * <pre class="code"> &lt;!-- will result in 10, which is the value of property 'age' of bean 'tb' --&gt;
 * &lt;util:property-path id="name" path="testBean.age"/&gt;</pre>
 *
 * Thanks to Matthias Ernst for the suggestion and initial prototype!
 *
 * @author Juergen Hoeller
 * @since 1.1.2
 * @see #setTargetObject
 * @see #setTargetBeanName
 * @see #setPropertyPath
 */
/**
 *｛@link FactoryBean｝，用于计算给定目标对象的属性路径。
 *
 *＜p＞目标对象可以直接指定，也可以通过bean名称指定。
 *
 *＜p＞用法示例：
 *
 *<pre-class=“code”>&lt；！--要通过名称引用的目标bean--&gt；
 *&lt；bean id=“tb”class=“org.springframework.beans.TestBean”singleton=“false”&gt；
 *&lt；property name=“age”value=“10”/&gt；
 *&lt；property name=“配偶”&gt；
 *&lt；bean class=“org.springframework.beans.TestBean”&gt；
 *&lt；property name=“age”value=“11”/&gt；
 *&lt/豆&gt；
 *&lt/属性&gt；
 *&lt/豆&gt；
 *
 *&lt；！--将得到12，这是内部bean的属性“age”的值--&gt；
 *&lt；bean id=“propertyPath1”class=“org.springframework.beans.factory.config.PropertyPathFactoryBean”&gt；
 *&lt；property name=“targetObject”&gt；
 *&lt；bean class=“org.springframework.beans.TestBean”&gt；
 *&lt；property name=“age”value=“12”/&gt；
 *&lt/豆&gt；
 *&lt/属性&gt；
 *&lt；property name=“propertyPath”value=“age”/&gt；
 *&lt/豆&gt；
 *
 *&lt；！--将得到11，这是bean“tb”的属性“配偶.age”的值--&gt；
 *&lt；bean id=“propertyPath2”class=“org.springframework.beans.factory.config.PropertyPathFactoryBean”&gt；
 *&lt；property name=“targetBeanName”value=“tb”/&gt；
 *&lt；property name=“propertyPath”value=“配偶.age”/&gt；
 *&lt/豆&gt；
 *
 *&lt；！--将得到10，这是bean“tb”的属性“age”的值--&gt；
 *&lt；bean id=“tb.age”class=“org.springframework.beans.factory.config.PropertyPathFactoryBean”/&gt</前>
 *
 *＜p＞如果您在配置文件中使用Spring2.0和XMLSchema支持，
 *您还可以使用以下配置样式访问属性路径。
 *（另请参阅Spring中标题为“基于XML模式的配置”的附录
 *更多示例请参考手册。）
 *
 *<pre-class=“code”>&lt；！--将得到10，这是bean“tb”的属性“age”的值--&gt；
 *&lt；util:property path id=“name”path=“testBean.age”/&gt</前>
 *
 *感谢Matthias Ernst的建议和最初的原型！
 *
 *@作者Juergen Hoeller
 *@自1.1.2
 *@参见#setTargetObject
 *@参见#setTargetBeanName
 *@参见#setPropertyPath
 */
public class PropertyPathFactoryBean implements FactoryBean<Object>, BeanNameAware, BeanFactoryAware {

	private static final Log logger = LogFactory.getLog(PropertyPathFactoryBean.class);

	@Nullable
	private BeanWrapper targetBeanWrapper;

	@Nullable
	private String targetBeanName;

	@Nullable
	private String propertyPath;

	@Nullable
	private Class<?> resultType;

	@Nullable
	private String beanName;

	@Nullable
	private BeanFactory beanFactory;


	/**
	 * Specify a target object to apply the property path to.
	 * Alternatively, specify a target bean name.
	 * @param targetObject a target object, for example a bean reference
	 * or an inner bean
	 * @see #setTargetBeanName
	 */
	/**
	 *指定要应用属性路径的目标对象。
	 *或者，指定目标bean名称。
	 *@param targetObject目标对象，例如bean引用
	 *或是一个内豆
	 *@参见#setTargetBeanName
	 */
	public void setTargetObject(Object targetObject) {
		this.targetBeanWrapper = PropertyAccessorFactory.forBeanPropertyAccess(targetObject);
	}

	/**
	 * Specify the name of a target bean to apply the property path to.
	 * Alternatively, specify a target object directly.
	 * @param targetBeanName the bean name to be looked up in the
	 * containing bean factory (e.g. "testBean")
	 * @see #setTargetObject
	 */
	/**
	 *指定要应用属性路径的目标bean的名称。
	 *或者，直接指定目标对象。
	 *@param targetBeanName要在
	 *包含bean工厂（例如“testBean”）
	 *@参见#setTargetObject
	 */
	public void setTargetBeanName(String targetBeanName) {
		this.targetBeanName = StringUtils.trimAllWhitespace(targetBeanName);
	}

	/**
	 * Specify the property path to apply to the target.
	 * @param propertyPath the property path, potentially nested
	 * (e.g. "age" or "spouse.age")
	 */
	/**
	 *指定要应用于目标的属性路径。
	 *@param propertyPath属性路径，可能嵌套
	 *（例如“年龄”或“配偶年龄”）
	 */
	public void setPropertyPath(String propertyPath) {
		this.propertyPath = StringUtils.trimAllWhitespace(propertyPath);
	}

	/**
	 * Specify the type of the result from evaluating the property path.
	 * <p>Note: This is not necessary for directly specified target objects
	 * or singleton target beans, where the type can be determined through
	 * introspection. Just specify this in case of a prototype target,
	 * provided that you need matching by type (for example, for autowiring).
	 * @param resultType the result type, for example "java.lang.Integer"
	 */
	/**
	 *指定计算属性路径的结果的类型。
	 *＜p＞注意：对于直接指定的目标对象，这不是必需的
	 *或单例目标bean，其中类型可以通过
	 *反省。只要在原型目标的情况下指定这一点，
	 *前提是需要按类型匹配（例如，自动布线）。
	 *@param resultType结果类型，例如“java.lang.Integer”
	 */
	public void setResultType(Class<?> resultType) {
		this.resultType = resultType;
	}

	/**
	 * The bean name of this PropertyPathFactoryBean will be interpreted
	 * as "beanName.property" pattern, if neither "targetObject" nor
	 * "targetBeanName" nor "propertyPath" have been specified.
	 * This allows for concise bean definitions with just an id/name.
	 */
	/**
	 *将解释此PropertyPathFactoryBean的bean名称
	 *如果既不是“targetObject”也不是
	 *未指定“targetBeanName”或“propertyPath”。
	 *这允许使用一个id/name来实现简洁的bean定义。
	 */
	@Override
	public void setBeanName(String beanName) {
		this.beanName = StringUtils.trimAllWhitespace(BeanFactoryUtils.originalBeanName(beanName));
	}


	@Override
	public void setBeanFactory(BeanFactory beanFactory) {
		this.beanFactory = beanFactory;

		if (this.targetBeanWrapper != null && this.targetBeanName != null) {
			throw new IllegalArgumentException("Specify either 'targetObject' or 'targetBeanName', not both");
		}

		if (this.targetBeanWrapper == null && this.targetBeanName == null) {
			if (this.propertyPath != null) {
				throw new IllegalArgumentException(
						"Specify 'targetObject' or 'targetBeanName' in combination with 'propertyPath'");
			}

			// No other properties specified: check bean name.
			// 未指定其他财产：检查bean名称。
			int dotIndex = (this.beanName != null ? this.beanName.indexOf('.') : -1);
			if (dotIndex == -1) {
				throw new IllegalArgumentException(
						"Neither 'targetObject' nor 'targetBeanName' specified, and PropertyPathFactoryBean " +
						"bean name '" + this.beanName + "' does not follow 'beanName.property' syntax");
			}
			this.targetBeanName = this.beanName.substring(0, dotIndex);
			this.propertyPath = this.beanName.substring(dotIndex + 1);
		}

		else if (this.propertyPath == null) {
			// either targetObject or targetBeanName specified
			// 指定了targetObject或targetBeanName
			throw new IllegalArgumentException("'propertyPath' is required");
		}

		if (this.targetBeanWrapper == null && this.beanFactory.isSingleton(this.targetBeanName)) {
			// Eagerly fetch singleton target bean, and determine result type.
			// 急切地获取单例目标bean，并确定结果类型。
			Object bean = this.beanFactory.getBean(this.targetBeanName);
			this.targetBeanWrapper = PropertyAccessorFactory.forBeanPropertyAccess(bean);
			this.resultType = this.targetBeanWrapper.getPropertyType(this.propertyPath);
		}
	}


	@Override
	@Nullable
	public Object getObject() throws BeansException {
		BeanWrapper target = this.targetBeanWrapper;
		if (target != null) {
			if (logger.isWarnEnabled() && this.targetBeanName != null &&
					this.beanFactory instanceof ConfigurableBeanFactory cbf &&
					cbf.isCurrentlyInCreation(this.targetBeanName)) {
				logger.warn("Target bean '" + this.targetBeanName + "' is still in creation due to a circular " +
						"reference - obtained value for property '" + this.propertyPath + "' may be outdated!");
			}
		}
		else {
			// Fetch prototype target bean...
			// 获取原型目标bean。。。
			Assert.state(this.beanFactory != null, "No BeanFactory available");
			Assert.state(this.targetBeanName != null, "No target bean name specified");
			Object bean = this.beanFactory.getBean(this.targetBeanName);
			target = PropertyAccessorFactory.forBeanPropertyAccess(bean);
		}
		Assert.state(this.propertyPath != null, "No property path specified");
		return target.getPropertyValue(this.propertyPath);
	}

	@Override
	public Class<?> getObjectType() {
		return this.resultType;
	}

	/**
	 * While this FactoryBean will often be used for singleton targets,
	 * the invoked getters for the property path might return a new object
	 * for each call, so we have to assume that we're not returning the
	 * same object for each {@link #getObject()} call.
	 */
	/**
	 *虽然该FactoryBean将经常用于单例目标，
	 *为属性路径调用的getter可能会返回一个新对象
	 *所以我们必须假设我们不会返回
	 *每个｛@link#getObject（）｝调用都使用相同的对象。
	 */
	@Override
	public boolean isSingleton() {
		return false;
	}

}
